package com.car.loan.project.xr.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.car.loan.common.exception.loan.LoanParameterException;
import com.car.loan.common.utils.BeanUtils;
import com.car.loan.common.utils.SecurityUtils;
import com.car.loan.framework.aspectj.lang.enums.BusinessStatus;
import com.car.loan.framework.web.domain.AjaxResult;
import com.car.loan.project.cm.domain.CmBank;
import com.car.loan.project.cm.mapper.CmBankMapper;
import com.car.loan.project.xr.domain.Customer;
import com.car.loan.project.xr.domain.Loan;
import com.car.loan.project.xr.domain.LoanCar;
import com.car.loan.project.xr.domain.LoanDetail;
import com.car.loan.project.xr.domain.LoanExt;
import com.car.loan.project.xr.domain.OverdueCustomerImportLog;
import com.car.loan.project.xr.domain.OverdueImportLog;
import com.car.loan.project.xr.domain.OverdueInfo;
import com.car.loan.project.xr.mapper.CustomerMapper;
import com.car.loan.project.xr.mapper.LoanCarMapper;
import com.car.loan.project.xr.mapper.LoanDetailMapper;
import com.car.loan.project.xr.mapper.LoanExtMapper;
import com.car.loan.project.xr.mapper.LoanMapper;
import com.car.loan.project.xr.mapper.OverdueCustomerImportLogMapper;
import com.car.loan.project.xr.mapper.OverdueImportLogMapper;
import com.car.loan.project.xr.mapper.OverdueInfoMapper;
import com.car.loan.project.xr.util.RedisDataUtil;
import com.car.loan.project.xr.util.bo.business.after.OverdueImportLogBo;
import com.car.loan.project.xr.util.bo.business.after.OverdueInputBo;
import com.car.loan.project.xr.util.enums.LoanBusinessStatus;
import com.car.loan.project.xr.util.enums.LoanStatusEnum;
import com.car.loan.project.xr.util.enums.OverdueCustomerImportStatus;
import com.car.loan.project.xr.util.enums.OverdueImportPattern;
import com.car.loan.project.xr.util.repayment.RepaymentPlan;
import com.car.loan.project.xr.util.repayment.RepaymentPlanParam;
import com.car.loan.project.xr.util.repayment.RepaymentPlanUtil;
import com.car.loan.project.xr.util.vo.after.OverdueImportLogVo;
import com.car.loan.project.xr.util.vo.after.OverdueInputVo;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author huangding
 * @date 2020/2/21 21:16
 */
@Service
public class OverdueService {

    @Resource
    private OverdueCustomerImportLogMapper overdueCustomerImportLogMapper;

    @Resource
    private OverdueImportLogMapper overdueImportLogMapper;

    @Resource
    private OverdueInfoMapper overdueInfoMapper;

    @Resource
    private CustomerMapper customerMapper;

    @Resource
    private LoanExtMapper loanExtMapper;

    @Resource
    private LoanDetailMapper loanDetailMapper;

    @Resource
    private LoanMapper loanMapper;

    @Resource
    private CmBankMapper cmBankMapper;

    @Resource
    private LoanCarMapper loanCarMapper;

    @Resource
    private RedisDataUtil redisDataUtil;


    /**
     * 导入逾期数据
     *
     * @param excelInfo 导入数据
     */
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult overdueImport(List<List<Object>> excelInfo, Long bankId, String dataDate,
        Integer pattern) {
        List<OverdueCustomerImportLog> overdueCustomerImportLogs = new ArrayList<>(
            excelInfo.size());
        excelInfo.forEach(data -> {
            String customerName = data.get(0).toString().trim().replace(" ", "");
            String cardNo = data.get(1).toString().trim().replace(" ", "");
            Double payableAmount = Double
                .parseDouble(data.get(2).toString().trim().replace(" ", ""));
            OverdueCustomerImportLog overdueCustomerImportLog = new OverdueCustomerImportLog();
            overdueCustomerImportLog.setCustomerName(customerName);
            overdueCustomerImportLog.setCardNo(cardNo);
            overdueCustomerImportLog.setPayableAmount(payableAmount);
            overdueCustomerImportLog.setStatus(OverdueCustomerImportStatus.SUCCESS.ordinal());
            List<Loan> loans = customerMapper
                .selectIdsByNameAndCardIdOrRepayCardNo(customerName, cardNo);
            if (loans == null || loans.size() == 0) {
                overdueCustomerImportLog.setStatus(OverdueCustomerImportStatus.ERROR.ordinal());
                overdueCustomerImportLog.setErrorMsg("未查询到匹配用户");
            } else if (loans.size() > 1) {
                overdueCustomerImportLog.setStatus(OverdueCustomerImportStatus.ERROR.ordinal());
                overdueCustomerImportLog.setErrorMsg("查询匹配到多个用户");
            } else {
                overdueCustomerImportLog.setCustomerId(loans.get(0).getCustomerId());
                overdueCustomerImportLog.setLoanId(loans.get(0).getId());
            }
            overdueCustomerImportLogs.add(overdueCustomerImportLog);
        });

        //插入导入记录
        OverdueImportLog overdueImportLog = new OverdueImportLog();
        overdueImportLog.setPattern(pattern);
        overdueImportLog.setDataDate(dataDate);
        overdueImportLog.setBankId(bankId);
        overdueImportLog.setAdminId(SecurityUtils.getLoginUserId());
        overdueImportLog.setImportTime(LocalDateTime.now());
        overdueImportLogMapper.insert(overdueImportLog);

        CmBank cmBank = cmBankMapper.selectById(bankId);
        Integer gracePeriod = cmBank.getGracePeriod();
        LocalDate localDtaDate = LocalDate.parse(dataDate);
        //处理用户记录
        overdueCustomerImportLogs.forEach(overdueCustomerImportLog -> {
            //插入导入用户记录
            overdueCustomerImportLog.setImportId(overdueImportLog.getId());
            if (OverdueCustomerImportStatus.SUCCESS.ordinal() == overdueCustomerImportLog
                .getStatus()) {
                //处理匹配数据
                handleMatchData(localDtaDate, overdueCustomerImportLog, gracePeriod);
            }
            overdueCustomerImportLogMapper.insert(overdueCustomerImportLog);
        });

        if (OverdueImportPattern.ALL.ordinal() == pattern) {
            //去掉本次更新数据
            Set<Long> importLoanIds = overdueCustomerImportLogs.stream()
                .filter(overdueCustomerImportLog -> overdueCustomerImportLog.getLoanId() != null)
                .map(OverdueCustomerImportLog::getLoanId).collect(
                    Collectors.toSet());
            //更新其他业务
            Loan queryLoan = new Loan();
            queryLoan.setBusinessStatus(LoanStatusEnum.OVERDUE_PAYMENT.status);
            QueryWrapper<Loan> queryWrapper = new QueryWrapper<>(queryLoan);
            if (importLoanIds.size() > 0) {
                queryWrapper.notIn("id", importLoanIds);
            }
            queryWrapper.select("id,business_status");
            List<Loan> loans = loanMapper.selectList(queryWrapper);
            for (Loan loan : loans) {
                loan.setBusinessStatus(LoanStatusEnum.NORMAL_PAYMENT.status);
                loanMapper.updateById(loan);

                LoanExt loanExt = new LoanExt();
                loanExt.setOverdueAmount(0D);
                loanExt.setOverdueTerm(0);
                UpdateWrapper<LoanExt> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("loan_id", loan.getId());
                loanExtMapper.update(loanExt, updateWrapper);
            }
        }
        return AjaxResult.success();
    }

    /**
     * 处理匹配数据
     */
    private void handleMatchData(LocalDate dataDate,
        OverdueCustomerImportLog overdueCustomerImportLog, Integer gracePeriod) {
        //更新用户逾期情况
        Optional<LoanExt> optionalLoanExt = loanExtMapper
            .selectByLoanId(overdueCustomerImportLog.getLoanId());
        if (optionalLoanExt.isPresent()) {
            LoanExt loanExt = optionalLoanExt.get();
            Optional<LoanDetail> optionalLoanDetail = loanDetailMapper
                .selectByLoanId(overdueCustomerImportLog.getLoanId());
            LoanDetail loanDetail = optionalLoanDetail.get();
            //获取当前期数
            RepaymentPlanParam repaymentPlanParam = new RepaymentPlanParam();
            repaymentPlanParam
                .setFinancingAmount(new BigDecimal(loanExt.getFinancingAmount()));
            BeanUtils.copyBeanProp(repaymentPlanParam, loanDetail);
            repaymentPlanParam.setTerm(Integer.parseInt(loanExt.getTerm().toString()));
            repaymentPlanParam.setFirstStatementDate(loanDetail.getFirstRepayDate());
            repaymentPlanParam.setGracePeriod(gracePeriod);

            if (null == loanDetail.getLastRepaymentDate()) {
                throw new NullPointerException("最后还款时间未设置");
            }
            RepaymentPlan currentRepaymentPlan = RepaymentPlanUtil
                .getCurrentRepaymentPlan(dataDate, repaymentPlanParam,
                    LocalDate.parse(loanDetail.getLastRepaymentDate()));

            //获取逾期金额
            //根据数据日期和帐单日可计算出最近的帐单日为第几期（期数），如期数大于还款期数的取还款期数，再根据最近的帐单日计算出还款日，

            BigDecimal overdueAmount = BigDecimal.ZERO;
            if (currentRepaymentPlan.getTerm()
                .equals(Integer.parseInt(loanExt.getTerm().toString()))) {
                //最后一期
                overdueAmount = new BigDecimal(
                    overdueCustomerImportLog.getPayableAmount().toString());
            } else {
                // 如还款日大于等于数据日期，
                // 当前逾期金额=应还金额-当期帐单的还款额（包括利息），期数=期数-1，否则，当前逾期金额=应还金额，期数=期数
                LocalDate repaymentDay = LocalDate
                    .parse(currentRepaymentPlan.getRepaymentDay());
                overdueAmount = new BigDecimal(
                    overdueCustomerImportLog.getPayableAmount().toString());
                if (repaymentDay.compareTo(dataDate) > 0) {
                    overdueAmount = new BigDecimal(
                        overdueCustomerImportLog.getPayableAmount())
                        .subtract(currentRepaymentPlan.getMonthRepayAmount(),
                            MathContext.DECIMAL128).setScale(2, BigDecimal.ROUND_HALF_UP);
                    currentRepaymentPlan.setTerm(currentRepaymentPlan.getTerm() - 1);
                }
            }
            if (overdueAmount.compareTo(BigDecimal.ZERO) <= 0) {
                overdueCustomerImportLog.setStatus(OverdueCustomerImportStatus.ERROR.ordinal());
                overdueCustomerImportLog.setErrorMsg("逾期金额小于等于0");
                return;
            }
            //获取最大逾期金额
            BigDecimal overdueMaxAmount = new BigDecimal(
                loanExt.getOverdueMaxAmount().toString());
            if (overdueAmount.compareTo(overdueMaxAmount) > 0) {
                overdueMaxAmount = overdueAmount;
            }
            //添加逾期记录
            Optional<OverdueInfo> optionalOverdueInfo = overdueInfoMapper
                .selectOneByLoanIdAndItem(loanDetail.getLoanId(),
                    currentRepaymentPlan.getTerm());
            overdueCustomerImportLog.setItem(currentRepaymentPlan.getTerm());
            OverdueInfo overdueInfo = optionalOverdueInfo.orElseGet(() -> {
                OverdueInfo newOverdueInfo = new OverdueInfo();
                newOverdueInfo.setLoanId(loanDetail.getLoanId());
                newOverdueInfo.setItem(currentRepaymentPlan.getTerm());
                return newOverdueInfo;
            });
            overdueInfo.setUpdateTime(LocalDateTime.now());
            overdueInfo.setUpdateId(SecurityUtils.getLoginUserId());
            overdueInfo.setPayableAmount(overdueCustomerImportLog.getPayableAmount());
            overdueInfo.setOverdueAmount(overdueAmount.doubleValue());
            overdueInfo.setMaxOverdueAmount(overdueMaxAmount.doubleValue());
            overdueInfo.setDataDate(dataDate.toString());
            if (null != overdueInfo.getId() && overdueInfo.getId() > 0) {
                overdueInfoMapper.updateById(overdueInfo);
            } else {
                overdueInfoMapper.insert(overdueInfo);
            }
            //更新业务主体数据
            //对比期数
            Integer overdueTerm = loanExt.getOverdueTerm();
            if (null == overdueTerm || (!overdueTerm.equals(currentRepaymentPlan.getTerm()))) {
                loanExt.setOverdueCount(loanExt.getOverdueCount() + 1);
            }
            loanExt.setOverdueTerm(currentRepaymentPlan.getTerm());
            loanExt.setOverdueMaxAmount(overdueMaxAmount.doubleValue());
            loanExt.setOverdueAmount(overdueAmount.doubleValue());
            loanExtMapper.updateById(loanExt);

            Loan loan = new Loan();
            loan.setId(overdueCustomerImportLog.getLoanId());
            loan.setBusinessStatus(LoanStatusEnum.OVERDUE_PAYMENT.status);
            loanMapper.updateById(loan);
            overdueCustomerImportLog.setStatus(OverdueCustomerImportStatus.SUCCESS.ordinal());
        } else {
            overdueCustomerImportLog.setStatus(OverdueCustomerImportStatus.ERROR.ordinal());
            overdueCustomerImportLog.setErrorMsg("业务数据无法匹配");
        }
    }


    public Page<OverdueImportLogVo> getOverdueImportLog(OverdueImportLogBo overdueImportLogBo) {
        Page page = new Page(overdueImportLogBo.getPageNum(), overdueImportLogBo.getPageSize());
        return overdueCustomerImportLogMapper.selectOverdueImportLogPage(page, overdueImportLogBo);
    }

    public OverdueInputVo getOverdueInputInfo(Long loanId) {

        Optional<Loan> optionalLoan
            = Optional.ofNullable(loanMapper.selectById(loanId));
        Loan loan = optionalLoan.orElseThrow(() -> new LoanParameterException("无该业务数据"));
        //获取数据源
        Optional<LoanCar> optionalLoanCar = loanCarMapper.selectByLoanId(loanId);
        LoanCar loanCar = optionalLoanCar
            .orElseThrow(() -> new LoanParameterException("业务数据车辆信息异常"));
        Optional<LoanExt> optionalLoanExt = loanExtMapper.selectByLoanId(loanId);
        LoanExt loanExt = optionalLoanExt
            .orElseThrow(() -> new LoanParameterException("业务数据扩展信息异常"));
        Optional<LoanDetail> optionalLoanDetail = loanDetailMapper.selectByLoanId(loanId);
        LoanDetail loanDetail = optionalLoanDetail
            .orElseThrow(() -> new LoanParameterException("业务数据详情信息异常"));

        Customer customer = customerMapper.selectById(loan.getCustomerId());
        OverdueInputVo overdueInputVo = new OverdueInputVo();
        overdueInputVo.setDealerName(redisDataUtil.getDealerName(loan.getDealerId()));
        overdueInputVo.setDeptName(redisDataUtil.getDeptName(loan.getDeptId()));
        overdueInputVo.setAdminName(redisDataUtil.getAdminName(loan.getAdminId()));
        overdueInputVo.setCustomerName(customer.getName());
        overdueInputVo.setIdCardNo(customer.getIdCardNo());
        overdueInputVo.setMobile(customer.getMobile());
        overdueInputVo.setBankName(redisDataUtil.getBankName(loan.getBankId()));
        overdueInputVo.setInstallmentAmount(loanExt.getInstallmentAmount());
        overdueInputVo.setTerm(loanExt.getTerm());
        overdueInputVo.setRate(loanExt.getRate());
        overdueInputVo.setFinancingAmount(loanExt.getFinancingAmount());
        overdueInputVo.setLendDate(loanDetail.getLendDate());
        overdueInputVo.setCarType(loanCar.getCarType());
        overdueInputVo.setCarNature(loanCar.getCarNature());
        overdueInputVo.setBankCarBrand(loanCar.getBankCarBrand());
        overdueInputVo.setPlateType(loanCar.getPlateType());
        overdueInputVo.setRegistOwner(loanCar.getRegistOwner());
        overdueInputVo.setPlateNo(loanCar.getPlateNo());
        return overdueInputVo;
    }

    public AjaxResult overdueInput(OverdueInputBo overdueInputBo) {
        Optional<Loan> optionalLoan
            = Optional.ofNullable(loanMapper.selectById(overdueInputBo.getLoanId()));
        Loan loan = optionalLoan.orElseThrow(() -> new LoanParameterException("无该业务数据"));
        OverdueCustomerImportLog overdueCustomerImportLog = new OverdueCustomerImportLog();

        overdueCustomerImportLog.setLoanId(overdueInputBo.getLoanId());
        overdueCustomerImportLog.setPayableAmount(overdueInputBo.getPayableAmount());
        CmBank cmBank = cmBankMapper.selectById(loan.getBankId());
        Integer gracePeriod = cmBank.getGracePeriod();
        handleMatchData(LocalDate.parse(overdueInputBo.getDataDate()), overdueCustomerImportLog,
            gracePeriod);
        if (OverdueCustomerImportStatus.ERROR.ordinal() == overdueCustomerImportLog.getStatus()) {
            return AjaxResult.error(overdueCustomerImportLog.getErrorMsg());
        }

        //插入导入记录
        OverdueImportLog overdueImportLog = new OverdueImportLog();
        overdueImportLog.setPattern(OverdueImportPattern.IMPORT.ordinal());
        overdueImportLog.setDataDate(overdueInputBo.getDataDate());
        overdueImportLog.setBankId(cmBank.getId());
        overdueImportLog.setAdminId(SecurityUtils.getLoginUserId());
        overdueImportLog.setImportTime(LocalDateTime.now());
        overdueImportLogMapper.insert(overdueImportLog);

        Customer customer = customerMapper.selectById(loan.getCustomerId());
        overdueCustomerImportLog.setCustomerId(customer.getId());
        overdueCustomerImportLog.setCustomerName(customer.getName());
        overdueCustomerImportLog.setCardNo(customer.getIdCardNo());
        overdueCustomerImportLog.setImportId(overdueImportLog.getId());
        overdueCustomerImportLog.setStatus(OverdueCustomerImportStatus.ARTIFICIAL.ordinal());
        overdueCustomerImportLogMapper.insert(overdueCustomerImportLog);
        return AjaxResult.success();
    }
}
